package com.itheima.service.impl;

import cn.hutool.core.collection.CollectionUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.OrderItem;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.itheima.mapper.EmpMapper;
import com.itheima.pojo.*;
import com.itheima.service.DeptService;
import com.itheima.service.EmpExprService;
import com.itheima.service.EmpLogService;
import com.itheima.service.EmpService;
import com.itheima.utils.JwtUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDateTime;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

@Slf4j
@Service
@Scope("singleton") //默认创建单例对象，可以省略不写,存放在ioc容器中，范围是IOC容器创建对象创建，IOC容器销毁对象销毁
//@Scope("prototype") //原型，多例创建对象，每次进行从容器里面获取一次创建一次对象，范围是获取的时候创建对象，交给开发者或者gc去销毁回收
//@Scope("request") //仅支持web环境中，对象创建存放在请求域中（一次请求内,没有存放在ioc容器中），前端请求开始创建请求对象HttpServletRequest,响应结束销毁对象。
                  //    写入请求域：request.setAttribute("empServiceImpl",对象)
                  //    读取请求域：request.getAttribute("empServiceImpl")
//@Scope("session") //仅支持web环境中，对象创建存放在会话域中（一个会话内,没有存放在ioc容器中），浏览器第一次访问服务器开始，直到浏览器或服务器关闭结束（服务器计算距离上一次请求超过30分钟销毁当前会话session对象）。
//    写入会话域：session.setAttribute("empServiceImpl",对象)
//    读取会话域：session.getAttribute("empServiceImpl")
//@Scope("application") //仅支持web环境中，对象创建存放在上下文域中（应用程序内,没有存放在ioc容器中），web服务器开始就创建对象，web服务器关闭就销毁对象。
//    写入上下文域：servletContext.setAttribute("empServiceImpl",对象)
//    读取上下文域：servletContext.getAttribute("empServiceImpl")
public class EmpServiceImpl extends ServiceImpl<EmpMapper,Emp> implements EmpService  {


    @Autowired
    private EmpExprService empExprService;

    @Autowired
    private DeptService deptService;

    @Override
    public PageResult<Emp> page(EmpQueryParam empQueryParam) {
    /*    ////1. 设置分页参数
        //PageHelper.startPage(empQueryParam.getPage(), empQueryParam.getPageSize());
        //
        ////2. 执行查询
        //List<Emp> empList = empMapper.list(empQueryParam);
        //Page<Emp> p = (Page<Emp>) empList;
        //
        ////3. 封装分页结果
        //return new PageResult<>(p.getTotal(), p.getResult());*/

        // ▶ 1. 构建分页条件（核心分页参数设置）
        // 创建 MP 分页对象，参数1：当前页码，参数2：每页条数
        Page<Emp> page = Page.of(empQueryParam.getPage(), empQueryParam.getPageSize());

        // ▶ 2. 设置排序规则（按更新时间倒序）
        // 使用 MP 的 OrderItem 构建排序条件（字段名需与数据库列名一致）
        page.addOrder(OrderItem.desc("update_time"));

        //构建查询条件
        //LambdaQueryWrapper<Emp> queryWrapper = new LambdaQueryWrapper<>();
        //ServiceImpl里面自带了一个lambdaQuery，，而lambdaQuery又实现了lambdaquerychainwrapper
        page = lambdaQuery().like(empQueryParam.getName() != null && !empQueryParam.getName().isEmpty(),
                         Emp::getName, empQueryParam.getName())
                 .eq(empQueryParam.getGender() != null, Emp::getGender, empQueryParam.getGender())
                .between(empQueryParam.getBegin() != null && empQueryParam.getEnd() != null, Emp::getEntryDate, empQueryParam.getBegin(), empQueryParam.getEnd())
                .page(page);
        // 构建查询条件并执行分页
        //page = lambdaQuery().like(empQueryParam.getName() != null && !empQueryParam.getName().isEmpty(), Emp::getName, empQueryParam.getName())
        //        .eq(empQueryParam.getGender() != null, Emp::getGender, empQueryParam.getGender())
        //        .between(empQueryParam.getBegin() != null && empQueryParam.getEnd() != null, Emp::getEntryDate, empQueryParam.getBegin(), empQueryParam.getEnd())
        //        .page(page);

        // ▶ 3. 执行分页查询（触发 SQL 拦截器自动拼接分页语句）
        // 调用 ServiceImpl 继承的 page(page) 方法执行查询
        //page = page(page);

        // ▶ 4. 处理部门名称关联（解决 N+1 查询问题优化）
        // 获取当前页数据列表
        List<Emp> empList = page.getRecords();
        //洒洒水

        // 使用 Hutool 工具类判断集合非空（更安全的空值处理）
        if (CollectionUtil.isNotEmpty(empList)) {
            // ▷ 4.1 提取所有部门ID（使用 Stream 去重优化查询）
            List<Integer> deptIds = empList.stream()
                    .map(Emp::getDeptId)    // 提取员工关联的部门ID
                    .distinct()             // 去重避免重复查询
                    .collect(Collectors.toList());// 🔥 终止操作：触发流执行

            // ▷ 4.2 批量查询部门信息（IN 查询提升性能）
            List<Dept> deptList = deptService.listByIds(deptIds);

            // ▷ 4.3 建立部门ID与名称的映射关系（快速查找优化）
            Map<Integer, String> deptMap = deptList.stream()
                    .collect(Collectors.toMap(Dept::getId, Dept::getName));

            // ▷ 4.4 遍历员工列表设置部门名称
            empList.forEach(emp -> {
                // 从映射表中直接获取部门名称（时间复杂度 O(1)）
                String deptName = deptMap.get(emp.getDeptId());
                emp.setDeptName(deptName);
            });
        }

        // ▶ 5. 构建分页响应对象
        // PageResult 需包含总记录数（page.getTotal()）和当前页数据（page.getRecords()）
        return new PageResult<>(page.getTotal(), page.getRecords());
    }

    @Autowired
    private EmpLogService empLogService;

    @Transactional(rollbackFor = Exception.class) //开启事务管理 - rollbackFor: 抛出什么异常回滚事务
    @Override
    public boolean save(Emp emp)  {
        //1. 保存员工的基本信息
        emp.setCreateTime(LocalDateTime.now());
        emp.setUpdateTime(LocalDateTime.now());
        save(emp);
        log.info("保存员工信息, {}", emp);

        //2. 保存员工的工作经历信息 --> 属于上面刚添加的员工的
        List<EmpExpr> exprList = emp.getExprList();
        if(exprList != null && !exprList.isEmpty()){ //有工作经历
            //遍历集合，设置员工id
            exprList.forEach(expr -> {
                expr.setEmpId(emp.getId());
            });
            empExprService.saveBatch(exprList);//批量插入员工经理
        }
        return true;
    }
    @Transactional(rollbackFor = Exception.class)
    @Override
    public void delete(List<Integer> ids) {
        //1. 删除员工的基本信息
        removeByIds(ids);

        //2. 删除员工的工作经历信息
        //DELETE FROM emp_expr WHERE emp_id IN (1, 2, 3, ...)
       empExprService.remove(Wrappers.<EmpExpr>lambdaQuery().in(EmpExpr::getEmpId,ids));
    }



    @Transactional(rollbackFor = Exception.class)
    @Override
    public void update(Emp emp) {
        //1. 根据ID更新员工基本信息
        emp.setUpdateTime(LocalDateTime.now());
       updateById(emp);//将update修改为updateById

        //2. 更新员工的工作经历信息(先删除, 再添加)
        //2.1 根据员工的id删除员工的工作经历
        empExprService.removeById(emp.getId());
        //2.2 再添加员工的工作经历 (批量)
        List<EmpExpr> exprList = emp.getExprList();
        if(exprList != null && !exprList.isEmpty()){ //有工作经历
            //遍历集合，设置员工id
            exprList.forEach(expr -> {
                expr.setEmpId(emp.getId());
            });
           empExprService.saveBatch(exprList);
        }
    }

    @Override
    public JobOption getEmpJobData() {
        // [{pos=教研主管, num=1} {pos=讲师, num=14}  {pos=学工主管, num=1} ...]
        /*List<Map<String,Object>> jobDataList = listMaps(query().select("(case\n" +
                "                    when job = 1 then '班主任'\n" +
                "                    when job = 2 then '讲师'\n" +
                "                    when job = 3 then '学工主管'\n" +
                "                    when job = 4 then '教研主管'\n" +
                "                    when job = 5 then '咨询师'\n" +
                "                    else '其他'\n" +
                "            end) as pos,\n" +
                "               count(*) as num ").groupBy("job").orderByAsc("num"));*/
        QueryWrapper<Emp> queryWrapper = new QueryWrapper<>();
        queryWrapper.select("(case\n" +
                "                    when job = 1 then '班主任'\n" +
                "                    when job = 2 then '讲师'\n" +
                "                    when job = 3 then '学工主管'\n" +
                "                    when job = 4 then '教研主管'\n" +
                "                    when job = 5 then '咨询师'\n" +
                "                    else '其他'\n" +
                "            end) as pos,\n" +
                "               count(*) as num ").groupBy("job").orderByAsc("num");
        List<Map<String,Object>> jobDataList = listMaps(queryWrapper);

        // 组装数据 JobOption
        List jobList = jobDataList.stream().map(map -> {
            return map.get("pos");
        }).toList();

        List dataList = jobDataList.stream().map(map -> {
            return map.get("num");
        }).toList();
        return new JobOption(jobList, dataList);
    }


    @Override
    public List<Map<String, Object>> getEmpGenderData() {
        List<Map<String, Object>> mapList = listMaps(
                new QueryWrapper<Emp>()
                        .select(
                                "IF(gender = 1, '男性员工', '女性员工') AS name",
                                "COUNT(*) AS value"
                        )
                        .groupBy("gender")
        );
      return  mapList;
    }

    /**
     * 根据用户名与密码查询员工登录信息
     * ctrl+Alt+鼠标点击，直接进入实现类重写方法中
     * @param emp
     * @return
     */
    @Override
    public LoginInfo getLoginInfo(Emp emp) {
        //1.根据用户名与密码查询员工信息Emp对象
        //Emp e = empMapper.findByUserNameAndPwd(emp);改

        QueryWrapper<Emp> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("username",emp.getUsername()).eq("password",emp.getPassword());
        Emp e = getOne(queryWrapper);

        if(e!=null) {
            //2.实例LoginInfo对象，封装数据返回
            LoginInfo loginInfo = new LoginInfo();
            loginInfo.setId(e.getId());
            loginInfo.setUsername(e.getUsername());
            loginInfo.setName(e.getName());

            //定义载荷
            Map<String,Object> claims = new HashMap<>();
            claims.put("id", e.getId());
            //调用工具类生成令牌
            String token = JwtUtils.generateJwt(claims);
            //封装令牌
            loginInfo.setToken(token);

            return loginInfo;
        }
        return null; //没有找到登录的员工
    }


}
